package cn.wen.java.io;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import cn.wen.java.lang.HelperOfString;
import cn.wen.java.nio.FileCharsetDetector;

/**
 * Title：文本文件编码转换器
 * <p>
 * Description：
 * <p>
 * Copyright：Copyright (c) 2010-2012
 * 
 * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
 * @version Revision：1.0 Date：2012/2/7 Description：初完成。
 *          <p>
 *          Revision：1.1 Date：2012/8/27 Description：整体优化，原始所有方法名弃用，统一采用convert命名。
 *          <p>
 */
public final class FileCharsetConverter {
    
    private static final Logger log = Logger.getLogger(FileCharsetConverter.class);

    /**
     * 将文件或目录中所有文件的内容，从（内部识别的）原始编码转换为目标编码。
     * 
     * @param file 文件或目录。
     * @param dstEncoding 目标编码。
     * @return 成功转换的文件数。
     * @throws IOException
     */
    public static int convert(File file, String dstEncoding) throws IOException {
	return convert(file, (String) null, dstEncoding);
    }
    
    /**
     * 将文件或目录中所有文件的内容，从原始编码转换为目标编码。
     * 
     * @param file 文件或目录。
     * @param srcEncoding 原始编码，注：为空时则在内部识别。
     * @param dstEncoding 目标编码。
     * @return 成功转换的文件数。
     * @throws IOException
     */
    public static int convert(File file, String srcEncoding, String dstEncoding) throws IOException {
	return convert(file, null, srcEncoding, dstEncoding);
    }

    /**
     * 将文件或目录中所有通过文件过滤器的文件的内容，从（内部识别的）原始编码转换为目标编码。
     * 
     * @param file 文件或目录。
     * @param filter 文件过滤器，注：为空时则不使用过滤器。
     * @param dstEncoding 目标编码。
     * @return 成功转换的文件数。
     * @throws IOException
     */
    public static int convert(File file, FileFilter filter, String dstEncoding) throws IOException {
	return convert(file, filter, null, dstEncoding);
    }
    
    /**
     * 将文件或目录中所有通过文件过滤器的文件的内容，从原始编码转换为目标编码。
     * 
     * @param file 文件或目录。
     * @param filter 文件过滤器，注：为空时则不使用过滤器。
     * @param srcEncoding 原始编码，注：为空时则在内部识别。
     * @param dstEncoding 目标编码。
     * @return 成功转换的文件数。
     * @throws IOException
     */
    public static int convert(File file, FileFilter filter, String srcEncoding, String dstEncoding) throws IOException {
	if (!file.exists()) {
	    return 0;
	}
	if (file.isFile()) {
	    return _convert(file, srcEncoding, dstEncoding);
	}
	return _convert(file, filter, srcEncoding, dstEncoding);
    }

    /**
     * 将文件内容，从原始编码转换为目标编码。
     * 
     * @param file 文件
     * @param srcEncoding 原始编码，注：为空时则在内部识别。
     * @param dstEncoding 目标编码
     * @return 处理标识 - 1：完成，0：取消、终止、错误。
     * @throws IOException
     */
    private static int _convert(File file, String srcEncoding, String dstEncoding) throws IOException {
	String path = file.getAbsolutePath();
	if (!file.canRead()) {
	    log.warn("文件不可读取，终止转换！ - " + path);
	    return 0;
	}
	if (!file.canWrite()) {
	    log.warn("文件不可写入，终止转换！ - " + path);
	    return 0;
	}

	if (StringUtils.isEmpty(srcEncoding)) {
	    srcEncoding = FileCharsetDetector.getEncoding(file);
	}
	if (srcEncoding.equals(dstEncoding) || srcEncoding.equals("ASCII")) {
	    log.debug("原始编码与目标编码一致的，或原始编码是ASCII码时无需转换。 - " + path);
	    return 0;
	}

	String data = HelperOfFile.read(file, srcEncoding);
	if (HelperOfFile.write(file, data, dstEncoding)) {
	    log.warn(HelperOfString.format("Conversion Successful({1} to {2}) - {0}", path, srcEncoding, dstEncoding));
	    return 1;
	}
	else {
	    log.warn(HelperOfString.format("Conversion failed({1} to {2}) - {0}", path, srcEncoding, dstEncoding));
	    return 0;
	}
    }

    /**
     * 将目录中所有通过文件过滤器的文件的内容，从原始编码转换为目标编码。
     * 
     * @param dir 目录。
     * @param filter 文件过滤器，注：为空时则不使用过滤器。
     * @param srcEncoding 原始编码，注：为空时则在内部识别。
     * @param dstEncoding 目标编码。
     * @return 成功转换的文件数。
     * @throws IOException
     */
    private static int _convert(File dir, FileFilter filter, String srcEncoding, String dstEncoding) throws IOException {
	File[] files = (filter == null) ? dir.listFiles() : dir.listFiles(filter);

	int count = 0;
	for (File file : files) {
	    if (file.isFile()) {
		count += _convert(file, srcEncoding, dstEncoding);
	    }
	    else {
		count += _convert(file, filter, srcEncoding, dstEncoding);
	    }
	}

	return count;
    }

}
